package com.lottery.game.core.modules.service.game.impl;

import com.lottery.game.core.modules.service.game.LockService;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;


/**
 * @author : xxx
 * @version : 1.0
 * Description :
 * Copyright : Copyright (c) 2018
 * Company : xxx Technology(Beijing) Chengdu Co. Ltd.
 * @date : 2018/6/14 下午2:08
 * @link :
 */
@Service
@Slf4j
public class LockServiceImpl implements LockService {

    /**
     * 将key 的值设为value ，当且仅当key 不存在，等效于 SETNX。
     */
    public static final String NX = "NX";

    /**
     * seconds — 以秒为单位设置 key 的过期时间，等效于EXPIRE key seconds
     */
    public static final String EX = "EX";

    /**
     * 调用set后的返回值
     */
    public static final String OK = "OK";

    /**
     * 默认锁的有效时间(s)
     */
    public static final int EXPIRE = 60;

    /**
     * 解锁的lua脚本
     */
    public static final String UNLOCK_LUA;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();
    }


    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Override
    public boolean lock(String key) {
        return lock(key, EXPIRE);
    }

    @Override
    public boolean lock(String key, String value, long expire) {
        return set(key, value, expire);
    }

    @Override
    public boolean lock(String key, long expire) {
        return lock(key, key, expire);
    }

    @Override
    public boolean tryLock(String key) {
        int loop = 0;
        while (true) {
            if (loop > 10) {
                break;
            }
            if (this.lock(key)) {
                return true;
            }
            loop ++;
            try {
                TimeUnit.MILLISECONDS.sleep(30);
            } catch (InterruptedException e) {
                log.error(e.getMessage(), e);
            }
        }
        return false;
    }

    @Override
    public boolean tryLock(String key, long expire) {
        return lock(key, EXPIRE);
    }

    @Override
    public boolean tryLock(String key, String value, long expire) {
        return lock(key, EXPIRE);
    }

    @Override
    public boolean unlock(String key) {
        return unlock(key, key);
    }

    @Override
    public boolean unlock(String key, String value) {
        //直接删除是有bug的，不过无所谓咯
        return redisTemplate.delete(key);
    }

    private boolean set(final String key, final String value, final long seconds) {
        Assert.isTrue(!StringUtils.isEmpty(key), "key不能为空");
        return redisTemplate.opsForValue().setIfAbsent(key, value, seconds, TimeUnit.SECONDS);

    }

    @Override
    public String getLockValue(String key) {
        return redisTemplate.opsForValue().get(key);
    }
}
